// package: rewords-server-2024
// filename: bsonBuilder
// author: diogoxiang@gmail.com
// time: 2024/11/5 17:34

package bsonBuilder

import (
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"strings"

	"github.com/gogf/gf/v2/util/gconv"
	"go.mongodb.org/mongo-driver/bson"
)

// BsonDToMap 转换 interface{} to map
// BsonDToMap 转换 interface{} to map
func BsonDToMap(bs interface{}) (map[string]interface{}, error) {
	var itemMap map[string]interface{}
	b, err := bson.Marshal(bs)
	if err != nil {
		return nil, fmt.Errorf("marshal error: %w", err)
	}
	if err := bson.Unmarshal(b, &itemMap); err != nil {
		return nil, fmt.Errorf("unmarshal error: %w", err)
	}
	return itemMap, nil
}

// BsonMToMap 转换 bson to map string
func BsonMToMap(bs *bson.M) map[string]interface{} {
	var itemMap map[string]interface{}
	b, _ := bson.Marshal(bs)
	bson.Unmarshal(b, &itemMap)
	return itemMap
}

// ExportBsonM 解析 *[]bson.M 统计类的
func ExportBsonM(arr *[]bson.M) map[string]interface{} {

	res := make(map[string]interface{}, len(*arr)) // 预分配容量
	for _, ms := range *arr {
		theType := gconv.String(ms["_id"])
		res[theType] = ms["count"]
	}
	return res
}

// ExportBsonS 解析 *bson.M 统计类的
func ExportBsonS(arr *bson.M) map[string]interface{} {

	res := map[string]interface{}{}
	//res["total"] = 0
	for key, ms := range *arr {
		//glog.Println(ms)
		//glog.Println(key)
		res[key] = ms
		//theType := gconv.String(ms["_id"])
		//res[theType] = ms["count"]
		//res["total"] = gconv.Int(ms["count"]) + gconv.Int(ms["total"])
	}
	return res
}

// ToBson transform interface to a bson Model
func ToBson(v interface{}) (mod *bson.M, err error) {
	data, err := bson.Marshal(v)
	if err != nil {
		return nil, errors.New("ECMarshal utils.ToBson")
	}

	err = bson.Unmarshal(data, &mod)
	if err != nil {
		return nil, errors.New("ECUnmarshal utils.ToBson")
	}
	return mod, nil
}

// ToMap transform interface to a bson Model
func ToMap(v interface{}) (mod map[string]interface{}, err error) {
	data, err := json.Marshal(v)
	if err != nil {
		return nil, errors.New("ECMarshal utils.ToMap")
	}

	err = json.Unmarshal(data, &mod)
	if err != nil {
		return nil, errors.New("ECUnmarshal utils.ToMap")
	}
	return mod, nil
}

// Copy  value as json
func Copy(toValue interface{}, fromValue interface{}) error {
	data, err := json.Marshal(fromValue)
	if err != nil {
		return errors.New("ECMarshal utils.Copy")
	}

	err = json.Unmarshal(data, toValue)
	if err != nil {
		return errors.New("ECUnmarshal utils.Copy")
	}

	return nil
}

func StructToBson(r interface{}) bson.M {
	result := make(bson.M)
	v := reflect.ValueOf(r)
	t := reflect.TypeOf(r)

	for i := 0; i < v.NumField(); i++ {
		filed := v.Field(i)
		tag := t.Field(i).Tag
		key := tag.Get("bson")
		if key == "" || key == "-" || key == "_id" {
			continue
		}
		keys := strings.Split(key, ",")
		if len(keys) > 0 {
			key = keys[0]
		}
		// TODO: 处理字段嵌套问题
		switch filed.Kind() {
		case reflect.Int, reflect.Int64:
			v := filed.Int()
			if v != 0 {
				result[key] = v
			}
		case reflect.String:
			v := filed.String()
			if v != "" {
				result[key] = v
			}
		case reflect.Bool:
			result[key] = filed.Bool()
		case reflect.Float64:
			v := filed.Float()
			if v != 0 {
				result[key] = v
			}
		case reflect.Float32:
			v := filed.Float()
			if v != 0 {
				result[key] = v
			}
		case reflect.Ptr:
			if !filed.IsNil() {
				elem := filed.Elem()
				if elem.Kind() == reflect.Struct {
					// 递归处理嵌套结构体
					nestedBson := StructToBson(elem.Interface())
					for k, v := range nestedBson {
						result[key+"."+k] = v
					}
				} else {
					result[key] = elem.Interface()
				}
			}

		// 添加对 Slice 和 Array 的支持
		case reflect.Slice, reflect.Array:
			if filed.Len() > 0 {
				result[key] = filed.Interface()
			}
		default:
		}
	}
	return result
}

func CopyT[T any](fromValue interface{}) (T, error) {
	var toValue T
	data, err := json.Marshal(fromValue)
	if err != nil {
		return toValue, fmt.Errorf("marshal error: %w", err)
	}

	if err := json.Unmarshal(data, &toValue); err != nil {
		return toValue, fmt.Errorf("unmarshal error: %w", err)
	}

	return toValue, nil
}
